package cn.schoolwow.quickserver.handler;

import cn.schoolwow.quickserver.domain.Client;
import cn.schoolwow.quickserver.response.HttpStatus;
import cn.schoolwow.quickserver.util.MIMEUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 静态资源处理
 */
public class StaticResourceHandler implements Handler {
    private static Logger logger = LoggerFactory.getLogger(StaticResourceHandler.class);
    /**
     * 默认首页
     */
    private static String[] indexHtmls = new String[]{"index.html", "index.htm", "index.jsp", "default.html", "default.htm"};

    @Override
    public Handler handle(Client client) throws IOException {
        getStaticResourcePath(client);
        if (!client.httpResponseMeta.headers.containsKey("ETag")) {
            client.httpResponseMeta.httpStatus(HttpStatus.NOT_FOUND);
            client.httpResponseMeta.contentLength = 0;
        }else{
            //支持分段下载
            client.httpResponseMeta.headers.put("Accept-Ranges", Arrays.asList("bytes"));
        }
        return new HttpResponseHandler();
    }

    /**
     * 获取静态资源路径
     */
    private void getStaticResourcePath(Client client) throws IOException {
        //从类路径获取
        getStaticResourcePath(client.httpRequestMeta.uri.getPath(), client);
        if (!client.httpResponseMeta.headers.containsKey("ETag") && client.httpRequestMeta.uri.getPath().endsWith("/")) {
            for (String indexHtml : indexHtmls) {
                getStaticResourcePath(client.httpRequestMeta.uri.getPath() + indexHtml, client);
                if (client.httpResponseMeta.headers.containsKey("ETag")) {
                    break;
                }
            }
        }
    }

    /**
     * 获取静态资源
     *
     * @param path   路径
     * @param client 请求信息
     */
    private void getStaticResourcePath(String path, Client client) throws IOException {
        //从指定的静态路径中寻找
        Set<Map.Entry<String,String>> entrySet = client.serverConfigMeta.staticResourcePathMap.entrySet();
        for (Map.Entry<String,String> entry: entrySet) {
            String staticResourcePath = entry.getKey();
            //判断路径前缀是否匹配
            if(!entry.getValue().isEmpty()){
                if(!client.httpRequestMeta.uri.getPath().startsWith(entry.getValue())){
                    logger.trace("[静态资源路径前缀不匹配]路径前缀:{},当前路径:{}",entry.getValue(),path);
                    continue;
                }
                logger.trace("[匹配静态资源路径前缀]请求路径:{},路径前缀:{}",path,entry.getValue());
                path = path.substring(entry.getValue().length());
            }

            Path resourcePath = Paths.get(entry.getKey() + "/" + path);
            if (Files.exists(resourcePath) && Files.isRegularFile(resourcePath)) {
                handleETag(Files.getLastModifiedTime(resourcePath).toMillis(), Files.size(resourcePath), client);
                handleContentType(resourcePath.getFileName().toString(), client);
                if (!handleIfNoneMatch(client)) {
                    client.httpResponseMeta.bodyInputStream = Files.newInputStream(resourcePath);
                }
                return;
            }else{
                logger.warn("[从指定路径中获取静态资源失败]请求地址:{},路径:{}", path, resourcePath);
            }
        }
        //从类路径中寻找
        path = client.serverConfigMeta.staticResourcePathPrefix + path;
        URL url = StaticResourceHandler.class.getResource(path);
        if (null != url) {
            switch (url.getProtocol()) {
                case "file": {
                    File file = new File(url.getFile());
                    if (file.isFile()) {
                        logger.trace("[文件资源]路径:{}", file.getAbsolutePath());
                        handleETag(file.lastModified(), file.length(), client);
                        handleContentType(file.getName(), client);
                        if (!handleIfNoneMatch(client)) {
                            client.httpResponseMeta.bodyInputStream = Files.newInputStream(file.toPath());
                        }
                        return;
                    }else{
                        logger.warn("[文件资源不存在]请求路径, 文件路径:{}", path, file.getAbsolutePath());
                    }
                }
                break;
                case "jar": {
                    JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                    JarFile jarFile = jarURLConnection.getJarFile();
                    if (path.startsWith("/")) {
                        path = path.substring(1);
                    }
                    JarEntry jarEntry = jarFile.getJarEntry(path);
                    if (jarEntry == null || jarEntry.isDirectory()) {
                        logger.warn("[jar资源]jar文件不存在或者为文件夹,路径:{},jarEntry:{}", path, jarEntry);
                        return;
                    }
                    logger.trace("[jar资源]路径:{}", jarEntry.getName());
                    handleETag(jarEntry.getLastModifiedTime().toMillis(), jarEntry.getSize(), client);
                    handleContentType(jarEntry.getName(), client);
                    if (!handleIfNoneMatch(client)) {
                        client.httpResponseMeta.bodyInputStream = jarFile.getInputStream(jarEntry);
                    }
                }
            }
        }else{
            logger.warn("[从类路径中获取静态资源失败]路径:{}", path);
        }
    }

    /**
     * 处理ETag头部
     *
     * @param lastModifiedTime 上次修改时间
     * @param fileSize         文件大小
     */
    private void handleETag(long lastModifiedTime, long fileSize, Client client) {
        String ETag = Long.toHexString(lastModifiedTime) + "-" + Long.toHexString(fileSize);
        client.httpResponseMeta.headers.put("ETag", Arrays.asList(ETag));
        client.httpResponseMeta.contentLength = fileSize;
    }

    /**
     * 处理Content-Type头部
     *
     * @param fileName 文件名
     */
    private void handleContentType(String fileName, Client client) {
        String mimeType = MIMEUtil.getMIMEType(fileName);
        logger.trace("[获取Content-Type]Content-Type:{},文件名:{}",mimeType, fileName);
        client.httpResponseMeta.contentType = mimeType;
    }

    /**
     * 是否浏览器已缓存
     */
    private boolean handleIfNoneMatch(Client client) {
        if (client.httpRequestMeta.headers.containsKey("If-None-Match")) {
            String oldETag = client.httpRequestMeta.headers.get("If-None-Match").get(0);
            String nowETag = client.httpResponseMeta.headers.get("ETag").get(0);
            if (oldETag.equals(nowETag)) {
                logger.trace("[浏览器已缓存静态资源]返回NOT_MODIFIED响应路径:{}", client.httpRequestMeta.uri);
                client.httpResponseMeta.httpStatus(HttpStatus.NOT_MODIFIED);
                return true;
            }
        }
        return false;
    }
}
